package util.input;

import util.core.Equation;
import util.exceptions.UnbalancableEquationException;
import util.exceptions.UnknownElementException;

import java.util.ArrayList;

public class Parser {


    public static Equation read(String eq) throws UnbalancableEquationException, UnknownElementException {
        ArrayList<String> left = new ArrayList<String>();
        ArrayList<String> right = new ArrayList<String>();

        parse(left, eq.substring(0, eq.indexOf("=")).replaceAll(" ", "") + " ");
        parse(right, eq.substring(eq.indexOf("=") + 1).replaceAll(" ", "") + " ");

        return new Equation(left, right);
    }

    private static void parse(ArrayList<String> side, String eq) {
        while (eq.contains("+")) {
            String[] result = getNext(eq);
            eq = result[1];
            side.add(result[0]);
        }
        if (eq.trim().length() != 0) {
            side.add(eq);
        }
    }

    private static String[] getNext(String eq) {
        String result[] = new String[2];
        int plusIndex = eq.indexOf("+");
        String molecule = eq.substring(0, plusIndex).trim();
        if (molecule.contains("{")) {
            result[0] = eq.substring(0, eq.indexOf("}") + 1).trim();
            eq = eq.substring(eq.indexOf("}") + 1);
            result[1] = eq.substring(eq.indexOf("+") + 1);
        } else if (plusIndex + 1 != eq.length()) {
            char next = eq.charAt(plusIndex + 1);
            if (noMoreMolecules(eq, plusIndex + 1)) {
                result[0] = eq.trim();
                result[1] = "";
            } else if (next == '+') {
                while (eq.charAt(plusIndex + 1) == '+') {
                    plusIndex++;
                }

                result[0] = eq.substring(0, plusIndex + 1).trim();
                eq = eq.substring(plusIndex + 1);
                result[1] = eq.substring(eq.indexOf("+") + 1);
            } else if (next > 47 && next < 58) {
                int end = findEndNum(eq, plusIndex + 1);
                result[0] = eq.substring(0, end);
                eq = eq.substring(end);
                result[1] = eq.substring(eq.indexOf("+") + 1);
            } else {
                result[0] = molecule;
                result[1] = eq.substring(eq.indexOf("+") + 1);
            }
        }
        return result;
    }

    private static boolean noMoreMolecules(String c, int start) {
        for (int i = start; i < c.length(); i++) {
            char current = c.charAt(i);
            if (isLetter(current) || current == '+' || current == '-')
                return false;
        }
        return true;
    }

    /**
     * Finds the index of the end of the element symbol
     *
     * @param c     The string which represents the Molecule
     * @param start The starting index
     * @return The end index
     */
    public static int findEndName(String c, int start) {
        start++;
        for (; start < c.length(); start++) {
            if (isNumber(c.charAt(start)) || (isUpperCase(c.charAt(start)))) {
                return start;
            }
        }
        return start;
    }

    public static String removeSurrounding(String c, String type) {
        String left = type.substring(0, 1), right = type.substring(1, 2);

        while (c.contains(left)) {
            String endString = "";
            int leftLocation = c.indexOf(left);
            int rightLocation = c.indexOf(right);
            String subMolecule = c.substring(leftLocation + 1, rightLocation);
            int startIndex;
            int endIndex = 0;

            String k = c.substring(rightLocation + 1, Parser.findEndNum(c, rightLocation + 1));
            int coeff;
            if (k.length() != 0)
                coeff = Integer.parseInt(k);
            else
                coeff = 1;
            c = c.substring(0, rightLocation + 1) + c.substring(Parser.findEndNum(c, rightLocation + 1));


            while (endIndex < subMolecule.length()) {
                startIndex = endIndex;
                endIndex = Parser.findEndName(subMolecule, startIndex);
                String temp;
                if (endIndex != subMolecule.length()) {
                    temp = subMolecule.substring(startIndex, endIndex);
                } else {
                    temp = subMolecule.substring(startIndex, endIndex);
                    endString = endString + temp + coeff;
                    break;
                }
                startIndex = endIndex;
                endIndex = Parser.findEndNum(subMolecule, startIndex);
                if (startIndex == endIndex) {
                    endString = endString + temp + coeff;
                } else {
                    endString = endString + temp + coeff * Integer.parseInt(subMolecule.substring(startIndex, endIndex));
                }
            }

            c = c.substring(0, leftLocation) + endString + c.substring(rightLocation + 1);
        }
        return c;
    }


    /**
     * Finds the index of the end of the element coefficient
     *
     * @param c     The string which represents the Molecule
     * @param start The starting index
     * @return The end index
     */
    public static int findEndNum(String c, int start) {
        for (; start < c.length(); start++) {
            if (!isNumber(c.charAt(start))) {
                return start;
            }
        }
        return start;
    }

    public static boolean isNumber(char c) {
        return c <= 57 && c >= 48;
    }

    public static boolean isUpperCase(char c) {
        return c <= 90 && c >= 65;
    }

    public static boolean isLowerCase(char c) {
        return c <= 122 && c >= 97;
    }

    public static boolean isLetter(char c) {
        return isLowerCase(c) || isUpperCase(c);
    }
}
